package com.spzx.product.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.spzx.common.core.exception.ServiceException;
import com.spzx.common.core.utils.bean.BeanUtils;
import com.spzx.common.redis.annotation.XingXingCache;
import com.spzx.product.api.domain.vo.SkuLockVo;
import com.spzx.product.api.domain.vo.SkuStockVo;
import com.spzx.product.domain.SkuStock;
import com.spzx.product.mapper.SkuStockMapper;
import com.spzx.product.service.SkuStockService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @ClassName SkuStockServiceImpl
 * @Author 波
 * @Date 2024/7/30 16:15
 */
@Service
public class SkuStockServiceImpl extends ServiceImpl<SkuStockMapper, SkuStock> implements SkuStockService {
    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    @XingXingCache(prefix = "product:skuStock")
    public SkuStockVo getSkuStockVo(Long skuId) {
        LambdaQueryWrapper<SkuStock> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(SkuStock::getSkuId, skuId);
        SkuStock skuStock = baseMapper.selectOne(queryWrapper);
        SkuStockVo skuStockVo = new SkuStockVo();
        BeanUtils.copyProperties(skuStock, skuStockVo);
        return skuStockVo;
    }

    /**
     * 锁定库存
     *
     * @param orderNo
     * @param skuLockVoList
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public String checkAndLock(String orderNo, List<SkuLockVo> skuLockVoList) {
        String skuStockKey = "sku:stock:checkAndLock:" + orderNo;
        String dataKey = "sku:skuLockVo:data:" + orderNo;
        //防止重复提交
        Boolean isExists = redisTemplate.opsForValue().setIfAbsent(skuStockKey, orderNo, 1, TimeUnit.HOURS);
        if (!isExists) {
            if (redisTemplate.hasKey(dataKey)) {
                return "";
            } else {
                return "重复提交！";
            }
        }
        //查询库存是否够用
        skuLockVoList.forEach(item -> {
            SkuStock skuStock = baseMapper.checkAndLock(item.getSkuId(), item.getSkuNum());
            if (skuStock != null) {
                item.setIsHaveStock(true);
            } else {
                item.setIsHaveStock(false);
            }
        });
        //只要有一个库存不够，所有锁定的商品都要解锁
        if (skuLockVoList.stream().anyMatch(skuLockVo -> !skuLockVo.getIsHaveStock())) {
            StringBuffer result = new StringBuffer();
            List<SkuLockVo> noHaveStockskuLockVoList = skuLockVoList.stream().filter(skuLockVo -> !skuLockVo.getIsHaveStock()).collect(Collectors.toList());
            for (SkuLockVo skuLockVo : noHaveStockskuLockVoList) {
                result.append(skuLockVo.getSkuId() + "号商品库存不足");
            }
            redisTemplate.delete(skuStockKey);
            return result.toString();
        } else {
            skuLockVoList.forEach(item -> {
                int row = baseMapper.lock(item.getSkuId(), item.getSkuNum());
                if (row == 0) {
                    redisTemplate.delete(skuStockKey);
                    throw new ServiceException("库存锁定失败");
                }
            });
        }
        // 如果所有商品都锁定成功的情况下，需要缓存锁定信息到redis。以方便将来解锁库存 或者 减库存
        redisTemplate.opsForValue().set(dataKey, skuLockVoList);
        return "锁定成功";
    }

    /**
     * 解锁库存
     *
     * @param orderNo
     */
    @Override
    public void unLockSkuStock(String orderNo) {
        String skuStockKey = "sku:stock:unLock:" + orderNo;
        String dataKey = "sku:skuLockVo:data:" + orderNo;
        Boolean isExist = redisTemplate.opsForValue().setIfAbsent(skuStockKey, orderNo, 1, TimeUnit.HOURS);
        if (!isExist) return;
        List<SkuLockVo> skuLockVoList = (List<SkuLockVo>) redisTemplate.opsForValue().get(dataKey);
        if (CollectionUtils.isEmpty(skuLockVoList)) return;
        for (SkuLockVo skuLockVo : skuLockVoList) {
            int row = baseMapper.unLock(skuLockVo.getSkuId(), skuLockVo.getSkuNum());
            if (row == 0) {
                redisTemplate.delete(skuStockKey);
                throw new ServiceException("解锁出库失败");
            }
        }
        redisTemplate.delete(dataKey);
    }

    /**
     * 扣减库存
     *
     * @param orderNo
     */
    @Override
    public void minusSkuStock(String orderNo) {
        String key = "sku:skuStock:minus" + orderNo;
        String dataKey = "sku:skuLockVo:data:" + orderNo;

        Boolean isExist = redisTemplate.opsForValue().setIfAbsent(key, orderNo, 1, TimeUnit.HOURS);
        if (!isExist) return;

        //获取库存消息
        List<SkuLockVo> skuLockVoList = (List<SkuLockVo>) redisTemplate.opsForValue().get(dataKey);
        if (CollectionUtils.isEmpty(skuLockVoList)) return;
        for (SkuLockVo skuLockVo : skuLockVoList) {
            int row = baseMapper.minusSkuStock(skuLockVo.getSkuId(), skuLockVo.getSkuNum());
            if (row == 0) {
                this.redisTemplate.delete(key);
                throw new ServiceException("减出库失败");
            }
        }
        // 解锁库存之后，删除锁定库存的缓存。以防止重复解锁库存
        this.redisTemplate.delete(dataKey);
    }


}
